/* Author: Kensaku KOMATSU

*/

/**
 * Collections
 *
 */

/////////////////////////////////////////////////
// _.each
// _.each(list, iterator, [context]) Alias: forEach
//
// list内の要素(element)に対し、iteratorで定義された関数処理を
// 繰り返し実行する。listがArrayの場合、iteratorに渡される
// 引数は(element, index, list), objectの場合は、
// (value, key, list)となる。
/////////////////////////////////////////////////

// listがArrayの場合
(function(){
  var sum = [0, 0];
  _.each([2,4,6], function(num, idx, list){
    sum[0] += num * this.a;
    sum[1] += idx;
  }, {a:3});
  console.log(sum);
  // #=> [36,3]
})();
// listがObjectの場合
(function(){
  var sum = [0, ""];
  _.each({a:2,b:4,c:6}, function(val, key, list){
    sum[0] += val * this.a;
    sum[1] += key;
  }, {a:3});
  console.log(sum);
  // #=> [36,"abc"]
})();


/////////////////////////////////////////////////
// _.map
// _.map(list, iterator, [context])
//
// list内の各要素にiteratorで定義された関数処理に従い、
// 新たなArrayを生成する。listがArrayの場合、iteratorに
// 渡される引数は、listの各要素値、Objectの場合は、
// （value, key)となる。
/////////////////////////////////////////////////

// listがArrayの場合
(function(){
  var arr = _.map([2,4,6], function(num){
    return num*this.a;
  }, {a:3});
  console.log(arr);
  // #=> [6,12,18]
})();
// listがObjectの場合
(function(){
  var arr = _.map({a:2,b:4,c:6}, function(val, key){
    return val * this.a;
  }, {a:3});
  console.log(arr);
  // #=> [6,12,18]
})();

/////////////////////////////////////////////////
// _.reduce
// _.reduce(list, iterator, memo, [context]) Aliases: inject, foldl
//
// listで与えられたArrayもしくはObjectから、iteratorで渡された関数処理に
// 従い、一つの値を得る。memoは、生成する値の初期値。
/////////////////////////////////////////////////

// listがArrayの場合
(function(){
  var ret = _.reduce([2,4,6], function(memo, num){
    return memo + num*this.a;
  }, 1, {a:3});
  console.log(ret);
  // #=> 37
})();
// listがObjectの場合
(function(){
  var ret = _.reduce({a:2,b:4,c:6}, function(memo, val, key){
    return memo + val * this.a;
  }, 1, {a:3});
  console.log(ret);
  // #=> 37
})();

/////////////////////////////////////////////////
// _.reduceRight
// _.reduceRight(list, iterator, memo, [context]) Alias: foldr
//
// reduceがlist内の要素を左から処理するのに対し、reduceRightでは
// 右から処理する。低速動作となるため、あまり有用ではない。
/////////////////////////////////////////////////

// listがArrayの場合
(function(){
  var ret = _.reduceRight([2,4,6], function(memo, num){
    return memo.concat(num * this.a);
  }, [], {a:3});
  console.log(ret);
  // #=> [18, 12, 6]
})();
// listがObjectの場合
(function(){
  var ret = _.reduceRight({a:2,b:4,c:6}, function(memo, val, key){
    return memo.concat(val * this.a);
  }, [], {a:3});
  console.log(ret);
  // #=> [18, 12, 6]
})();

/////////////////////////////////////////////////
// _.detect
// _.detect(list, iterator, [context])
//
// list内の各要素に対して、iteratorで定義された処理を実行し、
// 最初にtrueとなる要素を返す。trueが検出された時点で、iterator
// の処理は終了となり、listの全要素を処理することはない。
/////////////////////////////////////////////////

// listがArrayの場合
console.log("_detect");
(function(){
  var ret = _.detect([2,4,6], function(num){
    return num*this.a == 12;
  }, {a:3});
  console.log(ret);
  // #=> 4
})();
// listがObjectの場合
console.log("_detect");
(function(){
  var ret = _.detect({a:2,b:4,c:6}, function(val, key){
    return val*this.a == 12;
  }, {a:3});
  console.log(ret);
  // #=> 4
})();

/////////////////////////////////////////////////
// _.select
// _.select(list, iterator, [context]) Alias: filter
//
// list内の要素に対し、iteratorで定義された処理に対し、
// trueを返す要素のみの配列を返す。
/////////////////////////////////////////////////

// listがArrayの場合
console.log("_select");
(function(){
  var ret = _.select([2,3,4,5,6], function(num){
    return num % this.a == 0;
  }, {a:2});
  console.log(ret);
  // #=> [2, 4, 6]
})();
// listがObjectの場合
console.log("_select");
(function(){
  var ret = _.select({a:2,b:3,c:4}, function(val, key){
    return val % this.a == 0;
  }, {a:2});
  console.log(ret);
  // #=> [2, 4]
})();

/////////////////////////////////////////////////
// _.reject
// _.reject(list, iterator, [context]) Alias: filter
//
// _.selectと逆の動作。list内の要素に対し、iteratorで定義された
// 処理を実行し、true以外の要素の配列を返す。
/////////////////////////////////////////////////

// listがArrayの場合
console.log("_reject");
(function(){
  var ret = _.reject([2,3,4,5,6], function(num){
    return num % this.a == 0;
  }, {a:2});
  console.log(ret);
  // #=> [3,5]
})();
// listがObjectの場合
console.log("_reject");
(function(){
  var ret = _.reject({a:2,b:3,c:4}, function(val, key){
    return val % this.a == 0;
  }, {a:2});
  console.log(ret);
  // #=> [3]
})();

/////////////////////////////////////////////////
// _.all
// _.all(list, iterator, [context]) Alias: every
//
// list内の要素に対し、iteratorを実行。全要素が
// trueの時に trueを返す。それ以外はfalse
/////////////////////////////////////////////////

// listがArrayの場合
console.log("_all");
(function(){
  var ret = _.all([2,3,4,5,6], function(num){
    return num % this.a == 0;
  }, {a:2});
  console.log(ret);
  // #=> false
})();
// listがObjectの場合
console.log("_all");
(function(){
  var ret = _.all({a:2,b:3,c:4}, function(val, key){
    return val >= this.a;
  }, {a:2});
  console.log(ret);
  // #=> true
})();

/////////////////////////////////////////////////
// _.any
// _.any(list, iterator, [context]) Alias: some
//
// list内の要素に対し、iteratorを実行。一つでも
// trueがあれば、trueを返す。trueを見つけた時点で
// iterationは終了する。
/////////////////////////////////////////////////

// listがArrayの場合
console.log("_any");
(function(){
  var ret = _.any([2,3,4,5,6], function(num){
    return num % this.a == 0;
  }, {a:2});
  console.log(ret);
  // #=> true
})();
// listがObjectの場合
console.log("_any");
(function(){
  var ret = _.any({a:2,b:3,c:4}, function(val, key){
    return val < this.a;
  }, {a:2});
  console.log(ret);
  // #=> false
})();

/////////////////////////////////////////////////
// _.include
// _.include(list, value) Alias: contains
//
// list内にvalueがあれば、trueを返す(===で評価するため、
// 型も含めチェック）。listがArrayの場合、内部的には
// indexOfを用いている。
/////////////////////////////////////////////////

// listがArrayの場合
console.log("_include");
(function(){
  var ret = _.include([2,3,4,5,6], 3);
  console.log(ret);
  // #=> true
})();
// listがObjectの場合
console.log("_include");
(function(){
  var ret = _.include({a:2,b:3,c:4}, 3);
  console.log(ret);
  // #=> true
})();

/////////////////////////////////////////////////
// _.invoke
// _.invoke(list, methodName, [*arguments])
//
// list内の要素に対し、methodNameで与えられる関数を
// 実行する。argumentsは、methodNameの関数呼び出し。
/////////////////////////////////////////////////

// case 1
console.log("_invoke");
(function(){
  var ret = _.invoke([[3,2], [6,4]], "sort");
  console.log(ret);
  // #=> [[2,3], [4,6]]
})();
// case 2
console.log("_invoke");
(function(){
  var ret = _.invoke([[12,3],[21, 5]], "sort", function(a,b){return a-b});
  console.log(ret);
  // #=> [[3,12], [5,21]]
})();

/////////////////////////////////////////////////
// _.pluck
// _.pluck(list, propertyName)
//
// listに対し、propertyNameで指定された値の配列を
// 返す
/////////////////////////////////////////////////

//
console.log("_pluck");
(function(){
  var ret = _.pluck([{a:1, b:2}, {a:5, b:7}], "a");
  console.log(ret);
  // #=> [1,5]
})();

/////////////////////////////////////////////////
// _.max
// _.max(list, [iterator], [context])
//
// list中の最大値を返す。iteratorにより、判定基準を
// 指定可能
/////////////////////////////////////////////////

// case wo/ iterator
console.log("_max");
(function(){
  var ret = _.max([2,6,5]);
  console.log(ret);
  // #=> 6
})();
// case 2/ iterator
console.log("_max");
(function(){
  var ret = _.max([{name:"hoge", price:1000},{name:"fuga", price:500}], function(obj){return obj.price;});
  console.log(ret);
  // #=> {name:"hoge", price:1000}
})();

/////////////////////////////////////////////////
// _.min
// _.min(list, [iterator], [context])
//
// list中の最大値を返す。iteratorにより、判定基準を
// 指定可能
/////////////////////////////////////////////////

// case wo/ iterator
console.log("_min");
(function(){
  var ret = _.min([2,6,5]);
  console.log(ret);
  // #=> 2
})();
// case 2/ iterator
console.log("_min");
(function(){
  var ret = _.min([{name:"hoge", price:1000},{name:"fuga", price:500}], function(obj){return obj.price;});
  console.log(ret);
  // #=> {name:"fuga", price:500}
})();

/////////////////////////////////////////////////
// _.sortBy
// _.sortBy(list, iterator, [context])
//
// listに対し、iteratorの演算結果でソートする
/////////////////////////////////////////////////

console.log("_sortBy");
(function(){
  var ret = _.sortBy([{name:"hoge", price:1000},{name:"fuga",price:500}], function(obj){return obj.price});
  console.log(ret);
  // #=> [{name:"fuga",price:500},{name:"hoge",price:1000}]
})();

/////////////////////////////////////////////////
// _.sortedIndex
// _.sortedIndex(list, value, [iterator])
//
// listの中で、valueが何番目に入るかを返す。ソートされた
// listのオーダーを保持するのに有効。iteratorが渡された
// 場合は、listのソート計算に用いられる。
/////////////////////////////////////////////////

console.log("_sortedIndex");
(function(){
  var ret = _.sortedIndex([10,20,30,40], 35);
  console.log(ret);
  // #=> 3
})();

/////////////////////////////////////////////////
// _.toArray
// _.toArray(list)
//
// listを配列に変換する。argumentsの変換に便利。
/////////////////////////////////////////////////

console.log("_toArray");
(function(){
  var ret = (function(){ return _.toArray(arguments); })(1,2,3);
  console.log(ret);
  // #=> [1,2,3]
})();

/////////////////////////////////////////////////
// _.size
// _.size(list)
//
// listの長さを返す。
/////////////////////////////////////////////////

console.log("_size");
(function(){
  var ret = _.size({a:1, b:2, c:1});
  console.log(ret);
  // #=> 3
})();

/**
 * Arrays
 *
 */

/////////////////////////////////////////////////
// _.first
// _.first(array, [n]) Alias: head
//
// arrayの最初の要素を返す。nを指定した場合、最初から
// n個までの配列を返す。
/////////////////////////////////////////////////

console.log("_.first");
(function(){
  var ret = _.first([1,2,3,4,5]);
  console.log(ret);
  // #=> 1
  ret = _.first([1,2,3,4,5], 2);
  console.log(ret);
  // #=> [1,2]
})();

/////////////////////////////////////////////////
// _.rest
// _.rest(array, [n]) Alias: tail
//
// arrayの残りの要素を返す。nを指定すると、n番目以降の
// 配列が返される。
/////////////////////////////////////////////////

console.log("_.rest");
(function(){
  var ret = _.rest([1,2,3,4,5]);
  console.log(ret);
  // #=> 1
  ret = _.rest([1,2,3,4,5], 2);
  console.log(ret);
  // #=> [1,2]
})();

/////////////////////////////////////////////////
// _.last
// _.last(array)
//
// arrayの最後の要素を返す。
/////////////////////////////////////////////////

console.log("_.last");
(function(){
  var ret = _.last([1,2,3,4,5]);
  console.log(ret);
  // #=> 5
})();

/////////////////////////////////////////////////
// _.compact
// _.compact(array)
//
// arrayから、偽になる値を削除する。false, null, 0, "",
// undefinedとNaNが偽である。
/////////////////////////////////////////////////

console.log("_.compact");
(function(){
  var ret = _.compact([1,false,2,0,4,undefined]);
  console.log(ret);
  // #=> [1,2,4]
})();

/////////////////////////////////////////////////
// _.flatten
// _.flatten(array)
//
// ネストされた array をフラットにする。
/////////////////////////////////////////////////

console.log("_.flatten");
(function(){
  var ret = _.flatten([1,[2],[3, [[4]]]]);
  console.log(ret);
  // #=> [1,2,3,4]
})();

/////////////////////////////////////////////////
// _.without
// _.without(array, [*value])
//
// arrayからvalueで指定した値を削除する。値のチェックに
// === を用いている。
/////////////////////////////////////////////////

console.log("_.without");
(function(){
  var ret = _.without([1,2,0,3,2,"1"], 0, 1);
  console.log(ret);
  // #=> [2,3,2,"1"]
})();

/////////////////////////////////////////////////
// _.uniq
// _.uniq(array, [isSorted])
//
// arrayから重複値を削除し、ユニークな値による配列を返す。
// arrayがsortされているばあい、isSortedをtrueに設定すると、
// 高速なアルゴリズムが適用される。
/////////////////////////////////////////////////

console.log("_.uniq");
(function(){
  var ret = _.uniq([2,4,1,4,0,2]);
  console.log(ret);
  // #=> [2,4,1,0]
})();

/////////////////////////////////////////////////
// _.intersect
// _.intersect(*arrays)
//
// arraysの積集合（共通の値）による配列を返す。
/////////////////////////////////////////////////

console.log("_.intersect");
(function(){
  var ret = _.intersect([1,2,3], [3,1,4]);
  console.log(ret);
  // #=> [1,3]
})();

/////////////////////////////////////////////////
// _.zip
// _.zip(*arrays)
//
// arraysのindex値が一致する値をマージする。
/////////////////////////////////////////////////

console.log("_.zip");
(function(){
  var ret = _.zip(['a', 'b', 'c'], [30,40,50],[true, false, true]);
  console.log(ret);
  // #=> [['a',30,true],['b',40,false],['c',50,true]]
})();

/////////////////////////////////////////////////
// _.indexOf
// _.indexOf(array, value, [isSorted])
//
// arrayの中のvalueの位置を返す。valueが存在しない場合は、
// -1 を返す。arrayがソートされている場合は、isSortedを
// trueにすると、高速な binary search(二分探索）を行う。
/////////////////////////////////////////////////

console.log("_.indexOf");
(function(){
  var ret = _.indexOf([1,4,2], 4)
  console.log(ret);
  // #=> 1
})();

/////////////////////////////////////////////////
// _.lastIndexOf
// _.lastIndexOf(array, value)
//
// array中で、valueが最後に出現するインデックス値を返す。
// 見つからない場合は -1を返す。
/////////////////////////////////////////////////

console.log("_.lastIndexOf");
(function(){
  var ret = _.lastIndexOf([1,4,2,4], 4)
  console.log(ret);
  // #=> 3
})();

/////////////////////////////////////////////////
// _.range
// _.range([start], stop, [step])
//
// 整数値による連続した配列を返す。eachやmapに用いるのに
// 便利である。start が省略された場合のdefaultは0, stepが
// 省略された場合のdefaultは1である。startからstopまで、
// step毎に増加もしくは減少する整数値を返す。
/////////////////////////////////////////////////

console.log("_.range");
(function(){
  console.log(_.range(10));
  // #=> [0,1,2,3,4,5,6,7,8,9]
  console.log(_.range(1,11));
  // #=> [1,2,3,4,5,6,7,8,9,10]
  console.log(_.range(0,30,5));
  // #=> [0,5,10,15,20,25]
  console.log(_.range(0,-10,-1));
  // #=> [0,-1,-2,-3,-4,-5,-6,-7,-8,-9]
})();




/**
 * Functions
 *
 */

/////////////////////////////////////////////////
// _.bind
// _.bind(function, object, [*arguments])
//
// functionをobjectにbindする。このとき、thisはobjectを指す。
// argumentsをbindすること(カリー化)も可能。
/////////////////////////////////////////////////

var func = function(item){return item + " : "+this.price+" Yen"}
func = _.bind(func, {price: 100}, 'pen');
console.log(func());
// #=> 'pen : 100 Yen'

